#include "includes.h"
#include "tools.h"
#include "stm32f4xx.h"
#include "sys_timer.h"

#define SYMBOL_GLOBALS
#include "rtc.h"
#undef SYMBOL_GLOBALS

//* rtc模块初始化
void RTCInit(void)
{
	uint16_t usTimeout = 0; 
  RTC_InitTypeDef stRtcInit;
  
  //* 使能PWR时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
	
	//* 使能后备寄存器访问  
	PWR_BackupAccessCmd(ENABLE); 
  
  //* 使用外部低速晶振(LSE，32.768KHz)
  RCC_LSEConfig(RCC_LSE_ON); 
  
  //* 等待晶振就绪
  while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)	
  {				    
    DelaymsByCount(10);
    
    usTimeout++;
    //* 超过一定时间则认为初始化失败，外部晶振有问题？
    if(usTimeout >= 250)
      return;
  }  
  
  //* 设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
  
  //* 使能RTC时钟  
  RCC_RTCCLKCmd(ENABLE);
  
  //* 等待RTC寄存器同步
  RTC_WaitForSynchro();   
  
  //* 设置RTC分频及时间格式，分频计算公式：
  //* ck_spre(1Hz) = RTCCLK(LSE) / ((RTC_AsynchPrediv + 1) * (RTC_SynchPrediv + 1))
	stRtcInit.RTC_AsynchPrediv = 127;
	stRtcInit.RTC_SynchPrediv = 255;
	stRtcInit.RTC_HourFormat = RTC_HourFormat_24;
	RTC_Init(&stRtcInit); 
}

//* -------------------------------------------------------------
//* 设置系统时钟
//* -------------------------------------------------------------
void RTCSetSysTime(uint8_t ubYear, uint8_t ubMonth, uint8_t ubDay, uint8_t ubHour, uint8_t ubMin, uint8_t ubSec)
{  
	RTC_DateTypeDef stDate; 
  RTC_TimeTypeDef stTime;
  
  //* 设置日期
  stDate.RTC_Year    = GetBCDFromDecimal(ubYear);
	stDate.RTC_Month   = GetBCDFromDecimal(ubMonth);
	stDate.RTC_Date    = GetBCDFromDecimal(ubDay);
	stDate.RTC_WeekDay = CaculateWeekDay(ubYear, ubMonth, ubDay);   
	RTC_SetDate(RTC_Format_BCD, &stDate);
  
  //* 设置时间
  stTime.RTC_H12     = RTC_H12_AM;
	stTime.RTC_Hours   = GetBCDFromDecimal(ubHour);
	stTime.RTC_Minutes = GetBCDFromDecimal(ubMin);
	stTime.RTC_Seconds = GetBCDFromDecimal(ubSec);   
  RTC_SetTime(RTC_Format_BCD, &stTime);    
}

void RTCSetSysTimeBCD(uint8_t *pubBCDTime)
{
  RTC_DateTypeDef stDate; 
  RTC_TimeTypeDef stTime;
  
  //* 设置日期
  stDate.RTC_Year    = pubBCDTime[1];
	stDate.RTC_Month   = pubBCDTime[2];
	stDate.RTC_Date    = pubBCDTime[3];
	stDate.RTC_WeekDay = CaculateWeekDay((uint8_t)BCDToDecimal(pubBCDTime[1]), (uint8_t)BCDToDecimal(pubBCDTime[2]), (uint8_t)BCDToDecimal(pubBCDTime[3])); 
	RTC_SetDate(RTC_Format_BCD, &stDate);	
  
  //* 设置时间
  stTime.RTC_H12     = RTC_H12_AM;
	stTime.RTC_Hours   = pubBCDTime[4];
	stTime.RTC_Minutes = pubBCDTime[5];
	stTime.RTC_Seconds = pubBCDTime[6]; 
	RTC_SetTime(RTC_Format_BCD, &stTime); 
}

void RTCSetSystimeASCII(const char *pszASCIITime, uint8_t ubASCIIDecimalLen)
{
	uint8_t ubaBCDTime[7], ubBCDTimeLen;
	
	ubBCDTimeLen = ASCIIDecimalToBCDDecimal(pszASCIITime, ubASCIIDecimalLen, ubaBCDTime, sizeof(ubaBCDTime));
	if(ubBCDTimeLen != 7)
		return;
	
	if(ubaBCDTime[0] != 0x20 || ubaBCDTime[1] > 0x35 || ubaBCDTime[1] < 0x19 || ubaBCDTime[2] > 0x12 || ubaBCDTime[2] < 0x01 || 
		 ubaBCDTime[3] > 0x31  || ubaBCDTime[3] < 0x01 || ubaBCDTime[4] > 0x23 || ubaBCDTime[5] > 0x59 || ubaBCDTime[6] > 0x59)
		return;
	
	RTCSetSysTimeBCD(ubaBCDTime);	
}

void RTCSetSystemUnixTimestamp(time_t tUnixTimestamp)
{
	struct tm stTime;
	memcpy(&stTime, localtime((time_t*)&tUnixTimestamp), sizeof(struct tm));
  if(stTime.tm_year + 1900 < 2000)
    return;   
	RTCSetSysTime(stTime.tm_year + 1900 - 2000, stTime.tm_mon + 1, stTime.tm_mday, stTime.tm_hour, stTime.tm_min, stTime.tm_sec);  
}

void RTCUnixTimestampToTime(time_t tUnixTimestamp, PST_DATETIME pstDateTime)
{
	struct tm stTime;
	memcpy(&stTime, localtime((time_t*)&tUnixTimestamp), sizeof(struct tm));
	
	pstDateTime->usYear    = stTime.tm_year + 1900;
	pstDateTime->ubMonth   = stTime.tm_mon + 1;
	pstDateTime->ubDay     = stTime.tm_mday;
  pstDateTime->ubWeekDay = stTime.tm_wday ? stTime.tm_wday : 7; 
	pstDateTime->ubHour    = stTime.tm_hour;
	pstDateTime->ubMin     = stTime.tm_min;
	pstDateTime->ubSec     = stTime.tm_sec;	
}

void UnixTimeToLocalTime(time_t tUnixTimestamp, int8_t *pszDatetime)
{
  struct tm stTime;    
  localtime_r((time_t*)&tUnixTimestamp, &stTime);

  sprintf((char *)pszDatetime, "%d-%02d-%02d %02d:%02d:%02d", stTime.tm_year + 1900, stTime.tm_mon + 1, stTime.tm_mday, stTime.tm_hour, stTime.tm_min, stTime.tm_sec); 
}

//* 获取系统当前时间
void RTCGetSysTime(PST_DATETIME pstDateTime)
{
  RTC_DateTypeDef stDate; 
  RTC_TimeTypeDef stTime;
  
  //* 一定要先获取Time然后再获取Date
  RTC_GetTime(RTC_Format_BIN, &stTime); 
  RTC_GetDate(RTC_Format_BIN, &stDate);  
	
	pstDateTime->usYear    = stDate.RTC_Year + 2000; 
	pstDateTime->ubMonth   = stDate.RTC_Month; 
	pstDateTime->ubDay     = stDate.RTC_Date; 
  pstDateTime->ubWeekDay = stDate.RTC_WeekDay; 
	pstDateTime->ubHour    = stTime.RTC_Hours; 
	pstDateTime->ubMin     = stTime.RTC_Minutes; 
	pstDateTime->ubSec     = stTime.RTC_Seconds;
}

void RTCGetSysTimeBCD(uint8_t *pubBCDTime)
{	
	uint8_t ubaBCDVal[2];
	ST_DATETIME stDateTime;
		
	RTCGetSysTime(&stDateTime);
	
	//* 年
	DecimalToBCD(stDateTime.usYear, ubaBCDVal, 2);	
	pubBCDTime[0] = ubaBCDVal[0];
	pubBCDTime[1] = ubaBCDVal[1];
	
	//* 月
	DecimalToBCD(stDateTime.ubMonth, ubaBCDVal, 1);	
	pubBCDTime[2] = ubaBCDVal[0];
	
	//* 日
	DecimalToBCD(stDateTime.ubDay, ubaBCDVal, 1);	
	pubBCDTime[3] = ubaBCDVal[0];
	
	//* 时
	DecimalToBCD(stDateTime.ubHour, ubaBCDVal, 1);	
	pubBCDTime[4] = ubaBCDVal[0];
	
	//* 分
	DecimalToBCD(stDateTime.ubMin, ubaBCDVal, 1);	
	pubBCDTime[5] = ubaBCDVal[0];
	
	//* 秒
	DecimalToBCD(stDateTime.ubSec, ubaBCDVal, 1);	
	pubBCDTime[6] = ubaBCDVal[0];
}

uint32_t RTCGetDaySystimeDecimal(void)
{
	ST_DATETIME stDateTime;
	uint32_t unDayTimeDecimal;
	char szDecimal[10];
		
	RTCGetSysTime(&stDateTime);
	
	sprintf(szDecimal, "%02d%02d%02d", stDateTime.ubHour, stDateTime.ubMin, stDateTime.ubSec);
	unDayTimeDecimal = atoi(szDecimal);	
	return unDayTimeDecimal;
}

time_t UnixTimestamp(PST_DATETIME pstDateTime)
{
	struct tm stTM;
	stTM.tm_year = pstDateTime->usYear - 1900;
	stTM.tm_mon = pstDateTime->ubMonth - 1;
	stTM.tm_mday = pstDateTime->ubDay;
	stTM.tm_hour = pstDateTime->ubHour;
	stTM.tm_min = pstDateTime->ubMin;
	stTM.tm_sec = pstDateTime->ubSec;
	
	return mktime(&stTM);
}

time_t Time(void)
{
	ST_DATETIME stDateTime;
	
	RTCGetSysTime(&stDateTime);
	
	return UnixTimestamp(&stDateTime);
}
